home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / iguana / vts139b / lib / songelem.pas < prev    next >
Pascal/Delphi Source File  |  1993-09-04  |  20KB  |  647 lines

  1. UNIT SongElements;
  2.  
  3. INTERFACE
  4.  
  5. USES Objects, SoundDevices;
  6.  
  7.  
  8.  
  9.  
  10. {----------------------------------------------------------------------------}
  11. { Definitions for handling the format of individual notes.                   }
  12. { Notes are composed of four fields:                                         }
  13. {                                                                            }
  14. {   Period:     A number in the range 0..2047 which states the period of     }
  15. {               the note in units of 1/3584000 per sample. (this is a        }
  16. {               somewhat empyric number. If anyone knows the exact Amiga     }
  17. {               number, please, tell us). A zero means to keep using the     }
  18. {               same period used before.                                     }
  19. {   Instrument: A number in range 0..63 meaning the number of the instrument }
  20. {               which will be used for the note. A zero means use the same.  }
  21. {   Command:    A number (no real range) of the way the note should be       }
  22. {               played (i.e. Vibrato) a change in the playing sequence (i.e. }
  23. {               pattern break) or a change in the general parameters of the  }
  24. {               module player (i.e. set tempo). All the possible values are  }
  25. {               defined in the TModCommand enumerated type below.            }
  26. {   Parameter:  A parameter for the command. Its meaning differs from one    }
  27. {               command to another. Sometimes each nibble is considered as a }
  28. {               different parameter.                                         }
  29. {____________________________________________________________________________}
  30.  
  31. TYPE
  32.   TModCommand = (
  33.                  mcNone,       { 0 00 } { Just play the note, without any special option. }
  34.  
  35.                  mcArpeggio,   { 0 xy } { Rotate through three notes rapidly. }
  36.                  mcTPortUp,    { 1 xx } { Tone Portamento Up:   Gradual change of tone towards high frequencies. }
  37.                  mcTPortDown,  { 2 xx } { Tone Portamento Down: Gradual change of tone towards low  frequencies. }
  38.                  mcNPortamento,{ 3 xy } { Note Portamento:      Gradual change of tone towards a given note.     }
  39.                  mcVibrato,    { 4 xy } { Vibrato: Frequency changes around the note. }
  40.                  mcT_VSlide,   { 5 xy } { Tone Port. Up + Volume slide: Parameter means vol. slide. }
  41.                  mcVib_VSlide, { 6 xy } { Vibrato       + Volume slide: Parameter means vol. slide. }
  42.                  mcTremolo,    { 7 xy } { Tremolo: I don't know for sure. Fast volume variations, I think. }
  43.                  mcNPI1,       { 8 xx } { Do Nothing (as far as I know). }
  44.                  mcSampleOffs, { 9 xx } { Start the sample from the middle. }
  45.                  mcVolSlide,   { A xy } { Volume slide: Gradual change in volume. }
  46.                  mcJumpPattern,{ B xx } { End pattern and continue from a different pattern sequence position. }
  47.                  mcSetVolume,  { C xx } { Set the volume of the sound. }
  48.                  mcEndPattern, { D xx } { Continue at the start of the next pattern. }
  49.                  mcExtended,   { E xy } { Extended set of commands (ProTracker). }
  50.                  mcSetTempo,   { F xx } { Set the tempo of the music, in 1/50ths of a second. }
  51.  
  52.                  mcSetFilter,  { E 0x } { Set the output filter to the on or off value. }
  53.                  mcFinePortaUp,{ E 1x } { Like TPortUp,   but slower. }
  54.                  mcFinePortaDn,{ E 2x } { Like TPortDown, but slower. }
  55.                  mcGlissCtrl,  { E 3x } { ¿?¿?¿? }
  56.                  mcVibCtrl,    { E 4x } { Set the vibrato waveform. }
  57.                  mcFineTune,   { E 5x } { Fine tune the frequency of the sound. }
  58.                  mcJumpLoop,   { E 6x } { Make a loop inside a pattern. }
  59.                  mcTremCtrl,   { E 7x } { Set the tremolo waveform (I think). }
  60.                  mcNPI2,       { E 8x } { Do Nothing (as far as I know). }
  61.                  mcRetrigNote, { E 9x } { ¿?¿?¿? }
  62.                  mcVolFineUp,  { E Ax } { Like VolSlide, but slower and towards high frequencies. }
  63.                  mcVolFineDown,{ E Bx } { Like VolSlide, but slower and towards low  frequencies. }
  64.                  mcNoteCut,    { E Cx } { ¿?¿?¿? }
  65.                  mcNoteDelay,  { E Dx } { Wait a little before starting note. }
  66.                  mcPattDelay,  { E Ex } { ¿?¿?¿? }
  67.                  mcFunkIt,     { E Fx } { No idea, but sounds funny. }
  68.  
  69.                  mcOktArp,     {      } { Oktalizer arpeggio  }
  70.                  mcOktArp2,    {      } { Oktalizer arpeggio2 }
  71.  
  72.                  mcS3mRetrigNote,
  73.  
  74.                  mcLast
  75.   );
  76.  
  77. TYPE
  78.   PNoCommandNote = ^TNoCommandNote;
  79.   TNoCommandNote = RECORD
  80.     Instrument : BYTE;
  81.     Period     : WORD;
  82.     Volume     : BYTE;
  83.   END;
  84.  
  85.   PCommandNote = ^TCommandNote;
  86.   TCommandNote = RECORD
  87.     Command    : TModCommand;
  88.     Parameter  : BYTE;
  89.   END;
  90.  
  91.   PFullNote = ^TFullNote;
  92.   TFullNote = RECORD
  93.     CASE BYTE OF
  94.       0 : ( Instrument : BYTE;
  95.             Period     : WORD;
  96.             Volume     : BYTE;
  97.             Command    : TModCommand;
  98.             Parameter  : BYTE        );
  99.       1 : ( Note : TNoCommandNote;
  100.             Comm : TCommandNote      );
  101.   END;
  102.  
  103.  
  104.  
  105.  
  106. {----------------------------------------------------------------------------}
  107. { Definitions for handling the instruments used in the module.               }
  108. { Instruments are fragments of sampled sound (long arrays of bytes which     }
  109. { describe the wave of the sound of the instrument). The samples used in     }
  110. { music modules have a default volume and also, they can have a loop (for    }
  111. { sustained instruments) and a fine tuning constant (not yet implemented).   }
  112. {____________________________________________________________________________}
  113.  
  114. CONST
  115.   MaxSample      = 65520;
  116.   MaxInstruments = 255;
  117.  
  118.   LowQuality : BOOLEAN = TRUE;
  119.  
  120.   { Properties }
  121.  
  122.   ipMonoFreq = $0001;  { Set if the instrument is played always at the same freq (not implemented). }
  123.   ipLong     = $0002;  { Set if the instrument's sample is longer than 65520 bytes.                 }
  124.  
  125. TYPE
  126.   PSample = ^TSample;
  127.   TSample = ARRAY[0..MaxSample-1] OF SHORTINT;
  128.  
  129.   TIProperties = WORD; { Properties of the instrument. }
  130.  
  131.   PInstrumentRec = ^TInstrumentRec;
  132.   TInstrumentRec =
  133.     RECORD
  134.       Len,                  { Length of the instrument's sampled image.                           }
  135.       Reps,                 { Starting offset of the repeated portion.                            }
  136.       Repl  : LONGINT;      { Size of the repeated portion.                                       }
  137.       Vol   : BYTE;         { Default volume of the instrument (0..64)                            }
  138.       Ftune : BYTE;         { Fine tuning value for the instrument (not yet implemented).         }
  139.       NAdj  : WORD;         { Numerator of note adjutment.                                        }
  140.       DAdj  : WORD;         { Denominator of note adjutment.                                      }
  141.       Data  : ^TSample;     { Pointer to the first  65520 bytes of the sample.                    }
  142.       Xtra  : ^TSample;     { Pointer to the second 65520 bytes of the sample (if there is such). }
  143.       Prop  : TIProperties; { Bit mapped properties value.                                        }
  144.     END;
  145.  
  146.   PInstrument = ^TInstrument;
  147.   TInstrument =
  148.     OBJECT(TObject)
  149.       Name  : PString;
  150.       Instr : PInstrumentRec;
  151.  
  152.       CONSTRUCTOR Init;
  153.       DESTRUCTOR  Done; VIRTUAL;
  154.  
  155.       PROCEDURE FreeContents;
  156.       PROCEDURE Desample;
  157.       PROCEDURE Resample;
  158.  
  159.       PROCEDURE Change(Instrument : PInstrumentRec);
  160.       FUNCTION  GetName                             : STRING;
  161.       PROCEDURE SetName(S: STRING);
  162.     END;
  163.  
  164.  
  165.  
  166.  
  167. {----------------------------------------------------------------------------}
  168. { Definitions for handling the tracks of which patterns are built.           }
  169. { Tracks are lists of notes and command values of which the empty leading    }
  170. { and trailing blanks have been removed (obviated).                          }
  171. {____________________________________________________________________________}
  172.  
  173. TYPE
  174.   PNoteTrack = ^TNoteTrack;
  175.   TNoteTrack =
  176.     RECORD
  177.       NoteOffset : BYTE;
  178.       NumNotes   : BYTE;
  179.       Notes      : ARRAY[0..255] OF TNoCommandNote;
  180.     END;
  181.  
  182.   PCommTrack = ^TCommTrack;
  183.   TCommTrack =
  184.     RECORD
  185.       NoteOffset : BYTE;
  186.       NumNotes   : BYTE;
  187.       Notes      : ARRAY[0..255] OF TCommandNote;
  188.     END;
  189.  
  190.   PFullTrack = ^TFullTrack;
  191.   TFullTrack = ARRAY[0..255] OF TFullNote;
  192. {
  193.   PTrackCache = ^TTrackCache;
  194.   TTrackCache =
  195.     RECORD
  196.       InUse    : BOOLEAN;
  197.       Modified : BOOLEAN;
  198.       LastUse  : WORD;
  199.       Track    : PFullTrack;
  200.     END;
  201.  
  202. VAR
  203.   TrackCaches = ARRAY[1..MaxChannels] OF TTrackCache;
  204. }
  205. TYPE
  206.   PTrack = ^TTrack;
  207.   TTrack =
  208.     OBJECT(TObject)
  209.       Name : PString;
  210.       Note : PNoteTrack;
  211.       Comm : PCommTrack;
  212.  
  213.       CONSTRUCTOR Init;
  214.       DESTRUCTOR  Done; VIRTUAL;
  215.  
  216.       PROCEDURE FreeContents;
  217.  
  218.       PROCEDURE ChangeNote(At: WORD; VAR FullNote: TFullNote);
  219.       PROCEDURE GetNote   (At: WORD; VAR FullNote: TFullNote);
  220.  
  221.       PROCEDURE GetFullTrack(VAR Track: TFullTrack);
  222.       PROCEDURE SetFullTrack(VAR Track: TFullTrack);
  223.  
  224.       FUNCTION GetName : STRING;
  225.     END;
  226.  
  227.  
  228.  
  229.  
  230. {----------------------------------------------------------------------------}
  231. { Definitions for handling the format of the patterns.                       }
  232. { Patterns are arrays of pointers to tracks (up to 12 tracks).               }
  233. { A music module can have up to 255 individual patterns, arranged in a       }
  234. { sequence of up to 255.                                                     }
  235. { Empty patterns are not counted.                                            }
  236. {____________________________________________________________________________}
  237.  
  238. CONST
  239.   MaxSequence     = 256;
  240.   MaxPatterns     = 256;
  241.   MaxPatternLines = 256;
  242.   MaxChannels     = SoundDevices.MaxChannels;
  243.  
  244. TYPE
  245.   PPatternRec = ^TPatternRec;
  246.   TPatternRec =
  247.     RECORD
  248.       NNotes   : BYTE;
  249.       NChans   : BYTE;
  250.       Tempo    : BYTE;
  251.       BPM      : BYTE;
  252.       Channels : ARRAY[1..MaxChannels] OF WORD;
  253.     END;
  254.  
  255.   PPattern = ^TPattern;
  256.   TPattern =
  257.     OBJECT(TObject)
  258.       Name : PString;
  259.       Patt : PPatternRec;
  260.  
  261.       CONSTRUCTOR Init(Chans: WORD);
  262.       DESTRUCTOR  Done; VIRTUAL;
  263.  
  264.       PROCEDURE FreeContents;
  265.  
  266.       FUNCTION GetName : STRING;
  267.     END;
  268.  
  269.   PPatternSequence = ^TPatternSequence;
  270.   TPatternSequence = ARRAY[1..MaxSequence] OF BYTE;
  271.  
  272.  
  273.  
  274.  
  275. {----------------------------------------------------------------------------}
  276. { General definitions for the song.                                          }
  277. {____________________________________________________________________________}
  278.  
  279. TYPE
  280.   PSongComment = ^TSongComment;
  281.   TSongComment = ARRAY[1..16] OF STRING[60];
  282.  
  283.  
  284.  
  285.  
  286. IMPLEMENTATION
  287.  
  288. USES Heaps, GUS, Debugging, HexConversions;
  289.  
  290.  
  291.  
  292. {----------------------------------------------------------------------------}
  293. { TInstrument object implementation.                                         }
  294. {____________________________________________________________________________}
  295.  
  296. CONST
  297.   GUSAddr : LONGINT = 0;
  298.  
  299. CONSTRUCTOR TInstrument.Init;
  300.   BEGIN
  301.     TObject.Init;
  302.   END;
  303.  
  304.  
  305. DESTRUCTOR TInstrument.Done;
  306.   BEGIN
  307.     GUSAddr := 0;
  308.     SetName('');
  309.     FreeContents;
  310.     TObject.Done;
  311.   END;
  312.  
  313.  
  314. PROCEDURE TInstrument.FreeContents;
  315.   BEGIN
  316.     IF Instr = NIL THEN EXIT;
  317.  
  318.     IF NOT UsingGUS THEN
  319.       BEGIN
  320.         IF Instr^.Len > 65520 THEN
  321.           BEGIN
  322.             IF Instr^.Xtra <> NIL THEN
  323.               FullHeap.HFreeMem(POINTER(Instr^.Xtra), Instr^.Len - 65520);
  324.             Instr^.Len := 65520;
  325.           END;
  326.         IF Instr^.Data <> NIL THEN
  327.           FullHeap.HFreeMem(POINTER(Instr^.Data), Instr^.Len);
  328.       END;
  329.  
  330.     FullHeap.HFreeMem(POINTER(Instr), SizeOf(Instr^));
  331.   END;
  332.  
  333.  
  334. PROCEDURE TInstrument.Change(Instrument : PInstrumentRec);
  335.   CONST
  336.     Zero : BYTE = 0;
  337.   VAR
  338.     l : LONGINT;
  339.   BEGIN
  340.     FreeContents;
  341.     IF Instrument <> NIL THEN
  342.       BEGIN
  343.         FullHeap.HGetMem(POINTER(Instr), SizeOf(Instr^));
  344.         IF Instr <> NIL THEN
  345.           BEGIN
  346.             Move(Instrument^, Instr^, SizeOf(Instr^));
  347.             IF Instr^.Vol > 63 THEN
  348.               Instr^.Vol := 63;
  349.             IF Instr^.NAdj = 0 THEN
  350.               BEGIN
  351.                 Instr^.NAdj := $2000;
  352.                 Instr^.DAdj := $2000;
  353.               END;
  354.             IF Instr^.Repl <= 4 THEN
  355.               Instr^.Repl := 0;
  356.             IF LowQuality THEN
  357.               BEGIN
  358.                 Desample;
  359.                 Resample;
  360.               END;
  361.  
  362.             IF UsingGUS AND (Instr^.Data <> NIL) THEN
  363.               BEGIN
  364.                 l := Instr^.Len;
  365.                 IF l > 65520 THEN
  366.                   l := 65520;
  367.                 DumpToUltrasound(Instr^.Data^, l, GUSAddr, TRUE);
  368.                 FullHeap.HFreeMem(POINTER(Instr^.Data), l);
  369.                 Instr^.Data := POINTER(GUSAddr);
  370.                 INC(GUSAddr, l);
  371.                 IF l <> Instr^.Len THEN
  372.                   BEGIN
  373.                     l := Instr^.Len - l;
  374.                     DumpToUltrasound(Instr^.Xtra^, l, GUSAddr, TRUE);
  375.                     FullHeap.HFreeMem(POINTER(Instr^.Xtra), l);
  376.                     INC(GUSAddr, l);
  377.                   END;
  378. {
  379.                 IF Instr^.Repl = 0 THEN
  380.                   BEGIN
  381.                     DumpToUltrasound(Zero, 1, GUSAddr, TRUE);
  382.                     INC(GUSAddr);
  383.                     INC(Instr^.Len);
  384.                   END;
  385. }
  386.               END;
  387.  
  388.             IF Debug THEN
  389.               WriteLn(HexPtr(Instr^.Data));
  390.  
  391.           END;
  392.       END;
  393.   END;
  394.  
  395. FUNCTION TInstrument.GetName : STRING;
  396.   BEGIN
  397.     IF Name <> NIL THEN
  398.       GetName := Name^
  399.     ELSE
  400.       GetName := '';
  401.   END;
  402.  
  403.  
  404. PROCEDURE TInstrument.Desample;
  405.   VAR
  406.     w        : WORD;
  407.     p        : POINTER;
  408.     SizeFree : WORD;
  409.   BEGIN
  410.     WITH Instr^ DO
  411.       IF (Instr <> NIL) AND (Instr^.Data <> NIL)        AND
  412.          (Len > 128) AND ((Repl >= 2000) OR (Repl = 0)) THEN
  413.         BEGIN
  414.           FOR w := 0 TO Len DIV 2 - 1 DO
  415.             Data^[w] := (INTEGER(Data^[w*2]) +
  416.                          INTEGER(Data^[w*2+1])) DIV 2;
  417.  
  418.           p := Ptr(SEG(Data^), OFS(Data^) + Len DIV 2 + 7);
  419.           p := Ptr(SEG(p^) + (OFS(p^) SHR 4), OFS(p^) AND $8);
  420.  
  421.           SizeFree := Len -
  422.                       (WORD((SEG(p^) - SEG(Data^)) SHL 4) +
  423.                        WORD( OFS(p^) - OFS(Data^))        );
  424.  
  425.           FullHeap.HFreeMem(p, SizeFree);
  426.  
  427.           Len  := Len  DIV 2;
  428.           Reps := Reps DIV 2;
  429.           Repl := Repl DIV 2;
  430.           NAdj := NADJ  *  2;
  431.         END;
  432.   END;
  433.  
  434.  
  435.  
  436. PROCEDURE TInstrument.Resample;
  437.   VAR
  438.     w        : WORD;
  439.     p        : ^TSample;
  440.     SizeFree : WORD;
  441.   BEGIN
  442.     WITH Instr^ DO
  443.       IF (Instr <> NIL) AND (Instr^.Data <> NIL) AND
  444.          (Len < 128) AND (Repl > 0)              THEN
  445.         BEGIN
  446.           FullHeap.HGetMem(POINTER(p), Reps + Repl*3);
  447.           FOR w := 0 TO Reps+Repl-1 DO
  448.             p^[w] := Data^[w];
  449.  
  450.           FOR w := Reps TO Reps + Repl - 1 DO
  451.             BEGIN
  452.               p^[w+Repl  ] := Data^[w];
  453.               p^[w+Repl*2] := Data^[w];
  454.               p^[w+Repl*3] := Data^[w];
  455.             END;
  456.  
  457.           FullHeap.HFreeMem(POINTER(Data), Len);
  458.           Data := POINTER(p);
  459.  
  460.           Len  := Reps + Repl*4;
  461.           Repl := Repl * 4;
  462.         END;
  463.   END;
  464.  
  465.  
  466.  
  467. PROCEDURE TInstrument.SetName(S: STRING);
  468.   BEGIN
  469.     IF Name <> NIL THEN
  470.       FullHeap.HDisposeStr(Name);
  471.  
  472.     IF S <> '' THEN
  473.       Name := FullHeap.HNewStr(S);
  474.   END;
  475.  
  476.  
  477.  
  478. {----------------------------------------------------------------------------}
  479. { TTrack object implementation.                                              }
  480. {____________________________________________________________________________}
  481.  
  482. CONSTRUCTOR TTrack.Init;
  483.   BEGIN
  484.     TObject.Init;
  485.   END;
  486.  
  487.  
  488. DESTRUCTOR TTrack.Done;
  489.   BEGIN
  490.     FullHeap.HDisposeStr(Name);
  491.     FreeContents;
  492.     TObject.Done;
  493.   END;
  494.  
  495.  
  496. PROCEDURE TTrack.FreeContents;
  497.   BEGIN
  498.     IF Note <> NIL THEN
  499.       FullHeap.HFreeMem(POINTER(Note), Note^.NumNotes*SizeOf(TNoCommandNote) + 2);
  500.     IF Comm <> NIL THEN
  501.       FullHeap.HFreeMem(POINTER(Comm), Comm^.NumNotes*SizeOf(TCommandNote)   + 2);
  502.   END;
  503.  
  504.  
  505. PROCEDURE TTrack.ChangeNote(At: WORD; VAR FullNote: TFullNote);
  506.   VAR
  507.     Track : TFullTrack;
  508.   BEGIN
  509.     GetFullTrack(Track);
  510.     Track[At] := FullNote;
  511.     SetFullTrack(Track);
  512.   END;
  513.  
  514.  
  515. PROCEDURE TTrack.GetFullTrack(VAR Track: TFullTrack);
  516.   VAR
  517.     i : WORD;
  518.   BEGIN
  519.     FillChar(Track, SizeOf(Track), 0);
  520.  
  521.     IF Note <> NIL THEN
  522.       FOR i := 0 TO Note^.NumNotes DO
  523.         Track[i+Note^.NoteOffset].Note := Note^.Notes[i];
  524.  
  525.     IF Comm <> NIL THEN
  526.       FOR i := 0 TO Note^.NumNotes DO
  527.         Track[i+Note^.NoteOffset].Note := Note^.Notes[i];
  528.   END;
  529.  
  530.  
  531. PROCEDURE TTrack.SetFullTrack(VAR Track: TFullTrack);
  532.   VAR
  533.     i     : WORD;
  534.     MNote : TNoteTrack;
  535.     MComm : TCommTrack;
  536.   BEGIN
  537.     FillChar(MNote, SizeOf(MNote), 0);
  538.     FillChar(MComm, SizeOf(MComm), 0);
  539.     FOR i := 0 TO 255 DO
  540.       BEGIN
  541.         IF (Track[i].Instrument = 0) AND
  542.            (Track[i].Period     = 0) AND
  543.            (Track[i].Volume     = 0) THEN
  544.           BEGIN
  545.             IF MNote.NoteOffset = i THEN
  546.               INC(MNote.NoteOffset);
  547.           END
  548.         ELSE
  549.           BEGIN
  550.             MNote.NumNotes := i - MNote.NoteOffset + 1;
  551.             MNote.Notes[i - MNote.NoteOffset] := Track[i].Note;
  552.           END;
  553.  
  554.         IF Track[i].Command = mcNone THEN
  555.           BEGIN
  556.             IF MComm.NoteOffset = i THEN
  557.               INC(MComm.NoteOffset);
  558.           END
  559.         ELSE
  560.           BEGIN
  561.             MComm.NumNotes := i - MComm.NoteOffset + 1;
  562.             MComm.Notes[i - MComm.NoteOffset] := Track[i].Comm;
  563.           END;
  564.       END;
  565.  
  566.     FreeContents;
  567.  
  568.     FullHeap.HGetMem(POINTER(Note), MNote.NumNotes*SizeOf(TNoCommandNote) + 2);
  569.     FullHeap.HGetMem(POINTER(Comm), MComm.NumNotes*SizeOf(TCommandNote)   + 2);
  570.  
  571.     IF Note <> NIL THEN
  572.       Move(MNote, Note^, MNote.NumNotes*SizeOf(TNoCommandNote) + 2);
  573.     IF Comm <> NIL THEN
  574.       Move(MComm, Comm^, MComm.NumNotes*SizeOf(TCommandNote)   + 2);
  575.   END;
  576.  
  577.  
  578. PROCEDURE TTrack.GetNote(At: WORD; VAR FullNote: TFullNote);
  579.   BEGIN
  580.     DEC(At);
  581.     FillChar(FullNote, SizeOf(FullNote), 0);
  582.  
  583.     IF (Note <> NIL) AND (At >= Note^.NoteOffset) AND
  584.        (At < Note^.NoteOffset + Note^.NumNotes)   THEN
  585.       FullNote.Note := Note^.Notes[At - Note^.NoteOffset];
  586.  
  587.     IF (Comm <> NIL) AND (At >= Comm^.NoteOffset) AND
  588.        (At < Comm^.NoteOffset + Comm^.NumNotes)   THEN
  589.       FullNote.Comm := Comm^.Notes[At - Comm^.NoteOffset];
  590.   END;
  591.  
  592.  
  593. FUNCTION TTrack.GetName : STRING;
  594.   BEGIN
  595.     IF Name <> NIL THEN
  596.       GetName := Name^
  597.     ELSE
  598.       GetName := '';
  599.   END;
  600.  
  601.  
  602.  
  603.  
  604. {----------------------------------------------------------------------------}
  605. { TPattern object implementation.                                            }
  606. {____________________________________________________________________________}
  607.  
  608. CONSTRUCTOR TPattern.Init(Chans: WORD);
  609.   BEGIN
  610.     TObject.Init;
  611.  
  612.     FullHeap.HGetMem(POINTER(Patt), Chans*2 + 4);
  613.  
  614.     IF Patt <> NIL THEN
  615.       FillChar(Patt^, Chans*2 + 4, 0);
  616.     Patt^.NChans := Chans;
  617.   END;
  618.  
  619.  
  620. DESTRUCTOR TPattern.Done;
  621.   BEGIN
  622.     FullHeap.HDisposeStr(Name);
  623.     FreeContents;
  624.     TObject.Done;
  625.   END;
  626.  
  627.  
  628. PROCEDURE TPattern.FreeContents;
  629.   BEGIN
  630.     IF Patt <> NIL THEN
  631.       FullHeap.HFreeMem(POINTER(Patt), Patt^.NChans*2 + 4);
  632.   END;
  633.  
  634.  
  635. FUNCTION TPattern.GetName : STRING;
  636.   BEGIN
  637.     IF Name <> NIL THEN
  638.       GetName := Name^
  639.     ELSE
  640.       GetName := '';
  641.   END;
  642.  
  643.  
  644.  
  645.  
  646. END.
  647.